// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "cc/resources/resource_provider.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <deque>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>

#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "cc/output/buffer_to_texture_target_map.h"
#include "cc/resources/returned_resource.h"
#include "cc/resources/shared_bitmap_manager.h"
#include "cc/resources/single_release_callback.h"
#include "cc/test/test_context_provider.h"
#include "cc/test/test_gpu_memory_buffer_manager.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/test/test_texture.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/blocking_task_runner.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gpu_memory_buffer.h"

using testing::_;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;

namespace cc {
namespace {

    static const bool kUseGpuMemoryBufferResources = false;
    static const bool kEnableColorCorrectRendering = false;
    static const bool kDelegatedSyncPointsRequired = true;

    MATCHER_P(MatchesSyncToken, sync_token, "")
    {
        gpu::SyncToken other;
        memcpy(&other, arg, sizeof(other));
        return other == sync_token;
    }

    static void EmptyReleaseCallback(const gpu::SyncToken& sync_token,
        bool lost_resource,
        BlockingTaskRunner* main_thread_task_runner) { }

    static void ReleaseCallback(
        gpu::SyncToken* release_sync_token,
        bool* release_lost_resource,
        BlockingTaskRunner** release_main_thread_task_runner,
        const gpu::SyncToken& sync_token,
        bool lost_resource,
        BlockingTaskRunner* main_thread_task_runner)
    {
        *release_sync_token = sync_token;
        *release_lost_resource = lost_resource;
        *release_main_thread_task_runner = main_thread_task_runner;
    }

    static void SharedBitmapReleaseCallback(
        std::unique_ptr<SharedBitmap> bitmap,
        const gpu::SyncToken& sync_token,
        bool lost_resource,
        BlockingTaskRunner* main_thread_task_runner) { }

    static void ReleaseSharedBitmapCallback(
        std::unique_ptr<SharedBitmap> shared_bitmap,
        bool* release_called,
        gpu::SyncToken* release_sync_token,
        bool* lost_resource_result,
        const gpu::SyncToken& sync_token,
        bool lost_resource,
        BlockingTaskRunner* main_thread_task_runner)
    {
        *release_called = true;
        *release_sync_token = sync_token;
        *lost_resource_result = lost_resource;
    }

    static std::unique_ptr<SharedBitmap> CreateAndFillSharedBitmap(
        SharedBitmapManager* manager,
        const gfx::Size& size,
        uint32_t value)
    {
        std::unique_ptr<SharedBitmap> shared_bitmap = manager->AllocateSharedBitmap(size);
        CHECK(shared_bitmap);
        uint32_t* pixels = reinterpret_cast<uint32_t*>(shared_bitmap->pixels());
        CHECK(pixels);
        std::fill_n(pixels, size.GetArea(), value);
        return shared_bitmap;
    }

    class TextureStateTrackingContext : public TestWebGraphicsContext3D {
    public:
        MOCK_METHOD2(bindTexture, void(GLenum target, GLuint texture));
        MOCK_METHOD3(texParameteri, void(GLenum target, GLenum pname, GLint param));
        MOCK_METHOD1(waitSyncToken, void(const GLbyte* sync_token));
        MOCK_METHOD3(produceTextureDirectCHROMIUM,
            void(GLuint texture, GLenum target, const GLbyte* mailbox));
        MOCK_METHOD2(createAndConsumeTextureCHROMIUM,
            unsigned(GLenum target, const GLbyte* mailbox));

        // Force all textures to be consecutive numbers starting at "1",
        // so we easily can test for them.
        GLuint NextTextureId() override
        {
            base::AutoLock lock(namespace_->lock);
            return namespace_->next_texture_id++;
        }

        void RetireTextureId(GLuint) override { }

        GLuint64 insertFenceSync() override { return next_fence_sync_++; }

        void genSyncToken(GLuint64 fence_sync, GLbyte* sync_token) override
        {
            gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO, 0,
                gpu::CommandBufferId::FromUnsafeValue(0x123),
                fence_sync);
            sync_token_data.SetVerifyFlush();
            memcpy(sync_token, &sync_token_data, sizeof(sync_token_data));
        }

        GLuint64 GetNextFenceSync() const { return next_fence_sync_; }

        GLuint64 next_fence_sync_ = 1;
    };

    // Shared data between multiple ResourceProviderContext. This contains mailbox
    // contents as well as information about sync points.
    class ContextSharedData {
    public:
        static std::unique_ptr<ContextSharedData> Create()
        {
            return base::WrapUnique(new ContextSharedData());
        }

        uint32_t InsertFenceSync() { return next_fence_sync_++; }

        void GenMailbox(GLbyte* mailbox)
        {
            memset(mailbox, 0, GL_MAILBOX_SIZE_CHROMIUM);
            memcpy(mailbox, &next_mailbox_, sizeof(next_mailbox_));
            ++next_mailbox_;
        }

        void ProduceTexture(const GLbyte* mailbox_name,
            const gpu::SyncToken& sync_token,
            scoped_refptr<TestTexture> texture)
        {
            uint32_t sync_point = static_cast<uint32_t>(sync_token.release_count());

            unsigned mailbox = 0;
            memcpy(&mailbox, mailbox_name, sizeof(mailbox));
            ASSERT_TRUE(mailbox && mailbox < next_mailbox_);
            textures_[mailbox] = texture;
            ASSERT_LT(sync_point_for_mailbox_[mailbox], sync_point);
            sync_point_for_mailbox_[mailbox] = sync_point;
        }

        scoped_refptr<TestTexture> ConsumeTexture(const GLbyte* mailbox_name,
            const gpu::SyncToken& sync_token)
        {
            unsigned mailbox = 0;
            memcpy(&mailbox, mailbox_name, sizeof(mailbox));
            DCHECK(mailbox && mailbox < next_mailbox_);

            // If the latest sync point the context has waited on is before the sync
            // point for when the mailbox was set, pretend we never saw that
            // ProduceTexture.
            if (sync_point_for_mailbox_[mailbox] > sync_token.release_count()) {
                NOTREACHED();
                return scoped_refptr<TestTexture>();
            }
            return textures_[mailbox];
        }

    private:
        ContextSharedData()
            : next_fence_sync_(1)
            , next_mailbox_(1)
        {
        }

        uint64_t next_fence_sync_;
        unsigned next_mailbox_;
        using TextureMap = std::unordered_map<unsigned, scoped_refptr<TestTexture>>;
        TextureMap textures_;
        std::unordered_map<unsigned, uint32_t> sync_point_for_mailbox_;
    };

    class ResourceProviderContext : public TestWebGraphicsContext3D {
    public:
        static std::unique_ptr<ResourceProviderContext> Create(
            ContextSharedData* shared_data)
        {
            return base::WrapUnique(new ResourceProviderContext(shared_data));
        }

        GLuint64 insertFenceSync() override
        {
            return shared_data_->InsertFenceSync();
        }

        void genSyncToken(GLuint64 fence_sync, GLbyte* sync_token) override
        {
            gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO, 0,
                gpu::CommandBufferId::FromUnsafeValue(0x123),
                fence_sync);
            sync_token_data.SetVerifyFlush();
            // Commit the produceTextureCHROMIUM calls at this point, so that
            // they're associated with the sync point.
            for (const std::unique_ptr<PendingProduceTexture>& pending_texture :
                pending_produce_textures_) {
                shared_data_->ProduceTexture(pending_texture->mailbox, sync_token_data,
                    pending_texture->texture);
            }
            pending_produce_textures_.clear();
            memcpy(sync_token, &sync_token_data, sizeof(sync_token_data));
        }

        void waitSyncToken(const GLbyte* sync_token) override
        {
            gpu::SyncToken sync_token_data;
            if (sync_token)
                memcpy(&sync_token_data, sync_token, sizeof(sync_token_data));

            if (sync_token_data.release_count() > last_waited_sync_token_.release_count()) {
                last_waited_sync_token_ = sync_token_data;
            }
        }

        const gpu::SyncToken& last_waited_sync_token() const
        {
            return last_waited_sync_token_;
        }

        void texStorage2DEXT(GLenum target,
            GLint levels,
            GLuint internalformat,
            GLint width,
            GLint height) override
        {
            CheckTextureIsBound(target);
            ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
            ASSERT_EQ(1, levels);
            GLenum format = GL_RGBA;
            switch (internalformat) {
            case GL_RGBA8_OES:
                break;
            case GL_BGRA8_EXT:
                format = GL_BGRA_EXT;
                break;
            default:
                NOTREACHED();
            }
            AllocateTexture(gfx::Size(width, height), format);
        }

        void texImage2D(GLenum target,
            GLint level,
            GLenum internalformat,
            GLsizei width,
            GLsizei height,
            GLint border,
            GLenum format,
            GLenum type,
            const void* pixels) override
        {
            CheckTextureIsBound(target);
            ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
            ASSERT_FALSE(level);
            ASSERT_EQ(internalformat, format);
            ASSERT_FALSE(border);
            ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
            AllocateTexture(gfx::Size(width, height), format);
            if (pixels)
                SetPixels(0, 0, width, height, pixels);
        }

        void texSubImage2D(GLenum target,
            GLint level,
            GLint xoffset,
            GLint yoffset,
            GLsizei width,
            GLsizei height,
            GLenum format,
            GLenum type,
            const void* pixels) override
        {
            CheckTextureIsBound(target);
            ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
            ASSERT_FALSE(level);
            ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
            {
                base::AutoLock lock_for_texture_access(namespace_->lock);
                ASSERT_EQ(GLDataFormat(BoundTexture(target)->format), format);
            }
            ASSERT_TRUE(pixels);
            SetPixels(xoffset, yoffset, width, height, pixels);
        }

        void genMailboxCHROMIUM(GLbyte* mailbox) override
        {
            return shared_data_->GenMailbox(mailbox);
        }

        void produceTextureDirectCHROMIUM(GLuint texture,
            GLenum target,
            const GLbyte* mailbox) override
        {
            // Delay moving the texture into the mailbox until the next
            // sync token, so that it is not visible to other contexts that
            // haven't waited on that sync point.
            std::unique_ptr<PendingProduceTexture> pending(new PendingProduceTexture);
            memcpy(pending->mailbox, mailbox, sizeof(pending->mailbox));
            base::AutoLock lock_for_texture_access(namespace_->lock);
            pending->texture = UnboundTexture(texture);
            pending_produce_textures_.push_back(std::move(pending));
        }

        GLuint createAndConsumeTextureCHROMIUM(GLenum target,
            const GLbyte* mailbox) override
        {
            GLuint texture_id = createTexture();
            base::AutoLock lock_for_texture_access(namespace_->lock);
            scoped_refptr<TestTexture> texture = shared_data_->ConsumeTexture(mailbox, last_waited_sync_token_);
            namespace_->textures.Replace(texture_id, texture);
            return texture_id;
        }

        void GetPixels(const gfx::Size& size,
            ResourceFormat format,
            uint8_t* pixels)
        {
            CheckTextureIsBound(GL_TEXTURE_2D);
            base::AutoLock lock_for_texture_access(namespace_->lock);
            scoped_refptr<TestTexture> texture = BoundTexture(GL_TEXTURE_2D);
            ASSERT_EQ(texture->size, size);
            ASSERT_EQ(texture->format, format);
            memcpy(pixels, texture->data.get(), TextureSizeBytes(size, format));
        }

    protected:
        explicit ResourceProviderContext(ContextSharedData* shared_data)
            : shared_data_(shared_data)
        {
        }

    private:
        void AllocateTexture(const gfx::Size& size, GLenum format)
        {
            CheckTextureIsBound(GL_TEXTURE_2D);
            ResourceFormat texture_format = RGBA_8888;
            switch (format) {
            case GL_RGBA:
                texture_format = RGBA_8888;
                break;
            case GL_BGRA_EXT:
                texture_format = BGRA_8888;
                break;
            }
            base::AutoLock lock_for_texture_access(namespace_->lock);
            BoundTexture(GL_TEXTURE_2D)->Reallocate(size, texture_format);
        }

        void SetPixels(int xoffset,
            int yoffset,
            int width,
            int height,
            const void* pixels)
        {
            CheckTextureIsBound(GL_TEXTURE_2D);
            base::AutoLock lock_for_texture_access(namespace_->lock);
            scoped_refptr<TestTexture> texture = BoundTexture(GL_TEXTURE_2D);
            ASSERT_TRUE(texture->data.get());
            ASSERT_TRUE(xoffset >= 0 && xoffset + width <= texture->size.width());
            ASSERT_TRUE(yoffset >= 0 && yoffset + height <= texture->size.height());
            ASSERT_TRUE(pixels);
            size_t in_pitch = TextureSizeBytes(gfx::Size(width, 1), texture->format);
            size_t out_pitch = TextureSizeBytes(gfx::Size(texture->size.width(), 1), texture->format);
            uint8_t* dest = texture->data.get() + yoffset * out_pitch + TextureSizeBytes(gfx::Size(xoffset, 1), texture->format);
            const uint8_t* src = static_cast<const uint8_t*>(pixels);
            for (int i = 0; i < height; ++i) {
                memcpy(dest, src, in_pitch);
                dest += out_pitch;
                src += in_pitch;
            }
        }

        struct PendingProduceTexture {
            GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM];
            scoped_refptr<TestTexture> texture;
        };
        ContextSharedData* shared_data_;
        gpu::SyncToken last_waited_sync_token_;
        std::deque<std::unique_ptr<PendingProduceTexture>> pending_produce_textures_;
    };

    void GetResourcePixels(ResourceProvider* resource_provider,
        ResourceProviderContext* context,
        ResourceId id,
        const gfx::Size& size,
        ResourceFormat format,
        uint8_t* pixels)
    {
        resource_provider->WaitSyncTokenIfNeeded(id);
        switch (resource_provider->default_resource_type()) {
        case ResourceProvider::RESOURCE_TYPE_GPU_MEMORY_BUFFER:
        case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE: {
            ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
            ASSERT_NE(0U, lock_gl.texture_id());
            context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
            context->GetPixels(size, format, pixels);
            break;
        }
        case ResourceProvider::RESOURCE_TYPE_BITMAP: {
            ResourceProvider::ScopedReadLockSoftware lock_software(resource_provider,
                id);
            memcpy(pixels,
                lock_software.sk_bitmap()->getPixels(),
                lock_software.sk_bitmap()->getSize());
            break;
        }
        }
    }

    class ResourceProviderTest
        : public testing::TestWithParam<ResourceProvider::ResourceType> {
    public:
        explicit ResourceProviderTest(bool child_needs_sync_token)
            : shared_data_(ContextSharedData::Create())
            , context3d_(NULL)
            , child_context_(NULL)
            , main_thread_task_runner_(BlockingTaskRunner::Create(NULL))
        {
            switch (GetParam()) {
            case ResourceProvider::RESOURCE_TYPE_GPU_MEMORY_BUFFER:
            case ResourceProvider::RESOURCE_TYPE_GL_TEXTURE: {
                std::unique_ptr<ResourceProviderContext> context3d(
                    ResourceProviderContext::Create(shared_data_.get()));
                context3d_ = context3d.get();
                context_provider_ = TestContextProvider::Create(std::move(context3d));
                context_provider_->BindToCurrentThread();

                std::unique_ptr<ResourceProviderContext> child_context_owned = ResourceProviderContext::Create(shared_data_.get());
                child_context_ = child_context_owned.get();
                child_context_provider_ = TestContextProvider::Create(std::move(child_context_owned));
                child_context_provider_->BindToCurrentThread();
                break;
            }
            case ResourceProvider::RESOURCE_TYPE_BITMAP:
                break;
            }

            shared_bitmap_manager_.reset(new TestSharedBitmapManager);
            gpu_memory_buffer_manager_.reset(new TestGpuMemoryBufferManager);
            child_gpu_memory_buffer_manager_ = gpu_memory_buffer_manager_->CreateClientGpuMemoryBufferManager();

            resource_provider_ = base::MakeUnique<ResourceProvider>(
                context_provider_.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), main_thread_task_runner_.get(), 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting());
            child_resource_provider_ = base::MakeUnique<ResourceProvider>(
                child_context_provider_.get(), shared_bitmap_manager_.get(),
                child_gpu_memory_buffer_manager_.get(), main_thread_task_runner_.get(),
                0, 1, child_needs_sync_token, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting());
        }

        ResourceProviderTest()
            : ResourceProviderTest(true)
        {
        }

        static void CollectResources(ReturnedResourceArray* array,
            const ReturnedResourceArray& returned,
            BlockingTaskRunner* main_thread_task_runner)
        {
            array->insert(array->end(), returned.begin(), returned.end());
        }

        static ReturnCallback GetReturnCallback(ReturnedResourceArray* array)
        {
            return base::Bind(&ResourceProviderTest::CollectResources, array);
        }

        static void SetResourceFilter(ResourceProvider* resource_provider,
            ResourceId id,
            GLenum filter)
        {
            ResourceProvider::ScopedSamplerGL sampler(
                resource_provider, id, GL_TEXTURE_2D, filter);
        }

        ResourceProviderContext* context() { return context3d_; }

        ResourceId CreateChildMailbox(gpu::SyncToken* release_sync_token,
            bool* lost_resource,
            bool* release_called,
            gpu::SyncToken* sync_token)
        {
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                unsigned texture = child_context_->createTexture();
                gpu::Mailbox gpu_mailbox;
                child_context_->genMailboxCHROMIUM(gpu_mailbox.name);
                child_context_->produceTextureDirectCHROMIUM(texture, GL_TEXTURE_2D,
                    gpu_mailbox.name);
                child_context_->genSyncToken(child_context_->insertFenceSync(),
                    sync_token->GetData());
                EXPECT_TRUE(sync_token->HasData());

                std::unique_ptr<SharedBitmap> shared_bitmap;
                std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(base::Bind(
                    ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap),
                    release_called, release_sync_token, lost_resource));
                return child_resource_provider_->CreateResourceFromTextureMailbox(
                    TextureMailbox(gpu_mailbox, *sync_token, GL_TEXTURE_2D),
                    std::move(callback));
            } else {
                gfx::Size size(64, 64);
                std::unique_ptr<SharedBitmap> shared_bitmap(
                    CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, 0));

                SharedBitmap* shared_bitmap_ptr = shared_bitmap.get();
                std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(base::Bind(
                    ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap),
                    release_called, release_sync_token, lost_resource));
                return child_resource_provider_->CreateResourceFromTextureMailbox(
                    TextureMailbox(shared_bitmap_ptr, size), std::move(callback));
            }
        }

    protected:
        std::unique_ptr<ContextSharedData> shared_data_;
        ResourceProviderContext* context3d_;
        ResourceProviderContext* child_context_;
        scoped_refptr<TestContextProvider> context_provider_;
        scoped_refptr<TestContextProvider> child_context_provider_;
        std::unique_ptr<BlockingTaskRunner> main_thread_task_runner_;
        std::unique_ptr<TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
        std::unique_ptr<ResourceProvider> resource_provider_;
        std::unique_ptr<TestGpuMemoryBufferManager> child_gpu_memory_buffer_manager_;
        std::unique_ptr<ResourceProvider> child_resource_provider_;
        std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
    };

    void CheckCreateResource(ResourceProvider::ResourceType expected_default_type,
        ResourceProvider* resource_provider,
        ResourceProviderContext* context)
    {
        DCHECK_EQ(expected_default_type, resource_provider->default_resource_type());

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        EXPECT_EQ(1, static_cast<int>(resource_provider->num_resources()));
        if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            EXPECT_EQ(0u, context->NumTextures());

        uint8_t data[4] = { 1, 2, 3, 4 };
        resource_provider->CopyToResource(id, data, size);
        if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            EXPECT_EQ(1u, context->NumTextures());

        uint8_t result[4] = { 0 };
        GetResourcePixels(resource_provider, context, id, size, format, result);
        EXPECT_EQ(0, memcmp(data, result, pixel_size));

        resource_provider->DeleteResource(id);
        EXPECT_EQ(0, static_cast<int>(resource_provider->num_resources()));
        if (expected_default_type == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            EXPECT_EQ(0u, context->NumTextures());
    }

    TEST_P(ResourceProviderTest, Basic)
    {
        CheckCreateResource(GetParam(), resource_provider_.get(), context());
    }

    TEST_P(ResourceProviderTest, SimpleUpload)
    {
        gfx::Size size(2, 2);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(16U, pixel_size);

        ResourceId id = resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());

        uint8_t image[16] = { 0 };
        resource_provider_->CopyToResource(id, image, size);
        {
            uint8_t result[16] = { 0 };
            uint8_t expected[16] = { 0 };
            GetResourcePixels(resource_provider_.get(), context(), id, size, format,
                result);
            EXPECT_EQ(0, memcmp(expected, result, pixel_size));
        }

        for (uint8_t i = 0; i < pixel_size; ++i)
            image[i] = i;
        resource_provider_->CopyToResource(id, image, size);
        {
            uint8_t result[16] = { 0 };
            uint8_t expected[16] = {
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
            };
            GetResourcePixels(resource_provider_.get(), context(), id, size, format,
                result);
            EXPECT_EQ(0, memcmp(expected, result, pixel_size));
        }
    }

    TEST_P(ResourceProviderTest, TransferGLResources)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        gfx::ColorSpace color_space1 = gfx::ColorSpace::CreateSRGB();
        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format, color_space1);
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data2[4] = { 5, 5, 5, 5 };
        child_resource_provider_->CopyToResource(id2, data2, size);

        ResourceId id3 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        {
            ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
                child_resource_provider_.get(), id3);
            EXPECT_TRUE(lock.GetGpuMemoryBuffer());
        }

        GLuint external_texture_id = child_context_->createExternalTexture();

        gpu::Mailbox external_mailbox;
        child_context_->genMailboxCHROMIUM(external_mailbox.name);
        child_context_->produceTextureDirectCHROMIUM(
            external_texture_id, GL_TEXTURE_EXTERNAL_OES, external_mailbox.name);
        gpu::SyncToken external_sync_token;
        child_context_->genSyncToken(child_context_->insertFenceSync(),
            external_sync_token.GetData());
        EXPECT_TRUE(external_sync_token.HasData());

        gfx::ColorSpace color_space4 = gfx::ColorSpace::CreateXYZD50();
        TextureMailbox id4_mailbox(external_mailbox, external_sync_token,
            GL_TEXTURE_EXTERNAL_OES);
        id4_mailbox.set_color_space(color_space4);
        ResourceId id4 = child_resource_provider_->CreateResourceFromTextureMailbox(
            id4_mailbox,
            SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback)));

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));

        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);
            resource_ids_to_transfer.push_back(id3);
            resource_ids_to_transfer.push_back(id4);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(4u, list.size());
            EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
            EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
            EXPECT_EQ(list[0].mailbox_holder.sync_token,
                list[1].mailbox_holder.sync_token);
            EXPECT_TRUE(list[2].mailbox_holder.sync_token.HasData());
            EXPECT_EQ(list[0].mailbox_holder.sync_token,
                list[2].mailbox_holder.sync_token);
            EXPECT_EQ(external_sync_token, list[3].mailbox_holder.sync_token);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[0].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[1].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[2].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES),
                list[3].mailbox_holder.texture_target);
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id4));
            resource_provider_->ReceiveFromChild(child_id, list);
            EXPECT_NE(list[0].mailbox_holder.sync_token,
                context3d_->last_waited_sync_token());
            {
                resource_provider_->WaitSyncTokenIfNeeded(list[0].id);
                ResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
                    list[0].id);
            }
            EXPECT_EQ(list[0].mailbox_holder.sync_token,
                context3d_->last_waited_sync_token());
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_ids_to_receive.insert(id3);
            resource_ids_to_receive.insert(id4);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(4u, resource_provider_->num_resources());
        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        ResourceId mapped_id2 = resource_map[id2];
        ResourceId mapped_id3 = resource_map[id3];
        ResourceId mapped_id4 = resource_map[id4];
        EXPECT_NE(0u, mapped_id1);
        EXPECT_NE(0u, mapped_id2);
        EXPECT_NE(0u, mapped_id3);
        EXPECT_NE(0u, mapped_id4);
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id3));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id4));

        uint8_t result[4] = { 0 };
        GetResourcePixels(
            resource_provider_.get(), context(), mapped_id1, size, format, result);
        EXPECT_EQ(0, memcmp(data1, result, pixel_size));

        GetResourcePixels(
            resource_provider_.get(), context(), mapped_id2, size, format, result);
        EXPECT_EQ(0, memcmp(data2, result, pixel_size));

        EXPECT_FALSE(resource_provider_->IsOverlayCandidate(mapped_id1));
        EXPECT_FALSE(resource_provider_->IsOverlayCandidate(mapped_id2));
        EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id3));
        EXPECT_FALSE(resource_provider_->IsOverlayCandidate(mapped_id4));

        {
            resource_provider_->WaitSyncTokenIfNeeded(mapped_id1);
            ResourceProvider::ScopedReadLockGL lock1(resource_provider_.get(),
                mapped_id1);
            EXPECT_TRUE(lock1.color_space() == color_space1);

            resource_provider_->WaitSyncTokenIfNeeded(mapped_id4);
            ResourceProvider::ScopedReadLockGL lock4(resource_provider_.get(),
                mapped_id4);
            EXPECT_TRUE(lock4.color_space() == color_space4);
        }

        {
            // Check that transfering again the same resource from the child to the
            // parent works.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);
            resource_ids_to_transfer.push_back(id3);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_EQ(3u, list.size());
            EXPECT_EQ(id1, list[0].id);
            EXPECT_EQ(id2, list[1].id);
            EXPECT_EQ(id3, list[2].id);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[0].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[1].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[2].mailbox_holder.texture_target);
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            child_resource_provider_->ReceiveReturnsFromParent(returned);
            // ids were exported twice, we returned them only once, they should still
            // be in-use.
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
        }
        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            ASSERT_EQ(4u, returned_to_child.size());
            EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
            EXPECT_TRUE(returned_to_child[1].sync_token.HasData());
            EXPECT_TRUE(returned_to_child[2].sync_token.HasData());
            EXPECT_TRUE(returned_to_child[3].sync_token.HasData());
            EXPECT_FALSE(returned_to_child[0].lost);
            EXPECT_FALSE(returned_to_child[1].lost);
            EXPECT_FALSE(returned_to_child[2].lost);
            EXPECT_FALSE(returned_to_child[3].lost);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id1));
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id2));
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id3));
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id4));

        {
            child_resource_provider_->WaitSyncTokenIfNeeded(id1);
            ResourceProvider::ScopedReadLockGL lock(child_resource_provider_.get(),
                id1);
            ASSERT_NE(0U, lock.texture_id());
            child_context_->bindTexture(GL_TEXTURE_2D, lock.texture_id());
            child_context_->GetPixels(size, format, result);
            EXPECT_EQ(0, memcmp(data1, result, pixel_size));
        }
        // Ensure copying to resource doesn't fail.
        child_resource_provider_->CopyToResource(id2, data2, size);
        {
            child_resource_provider_->WaitSyncTokenIfNeeded(id3);
            ResourceProvider::ScopedReadLockGL lock(child_resource_provider_.get(),
                id3);
            ASSERT_NE(0U, lock.texture_id());
            child_context_->bindTexture(GL_TEXTURE_2D, lock.texture_id());
        }
        {
            // Transfer resources to the parent again.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);
            resource_ids_to_transfer.push_back(id3);
            resource_ids_to_transfer.push_back(id4);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(4u, list.size());
            EXPECT_EQ(id1, list[0].id);
            EXPECT_EQ(id2, list[1].id);
            EXPECT_EQ(id3, list[2].id);
            EXPECT_EQ(id4, list[3].id);
            EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
            EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
            EXPECT_TRUE(list[2].mailbox_holder.sync_token.HasData());
            EXPECT_TRUE(list[3].mailbox_holder.sync_token.HasData());
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[0].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[1].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[2].mailbox_holder.texture_target);
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_EXTERNAL_OES),
                list[3].mailbox_holder.texture_target);
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id4));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_ids_to_receive.insert(id3);
            resource_ids_to_receive.insert(id4);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(0u, returned_to_child.size());

        EXPECT_EQ(4u, resource_provider_->num_resources());
        resource_provider_->DestroyChild(child_id);
        EXPECT_EQ(0u, resource_provider_->num_resources());

        ASSERT_EQ(4u, returned_to_child.size());
        EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
        EXPECT_TRUE(returned_to_child[1].sync_token.HasData());
        EXPECT_TRUE(returned_to_child[2].sync_token.HasData());
        EXPECT_TRUE(returned_to_child[3].sync_token.HasData());
        EXPECT_FALSE(returned_to_child[0].lost);
        EXPECT_FALSE(returned_to_child[1].lost);
        EXPECT_FALSE(returned_to_child[2].lost);
        EXPECT_FALSE(returned_to_child[3].lost);
    }

#if defined(OS_ANDROID)
    TEST_P(ResourceProviderTest, OverlayPromotionHint)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        GLuint external_texture_id = child_context_->createExternalTexture();

        gpu::Mailbox external_mailbox;
        child_context_->genMailboxCHROMIUM(external_mailbox.name);
        child_context_->produceTextureDirectCHROMIUM(
            external_texture_id, GL_TEXTURE_EXTERNAL_OES, external_mailbox.name);
        gpu::SyncToken external_sync_token;
        child_context_->genSyncToken(child_context_->insertFenceSync(),
            external_sync_token.GetData());
        EXPECT_TRUE(external_sync_token.HasData());

        TextureMailbox id1_mailbox(external_mailbox, external_sync_token,
            GL_TEXTURE_EXTERNAL_OES);
        id1_mailbox.set_wants_promotion_hint(true);
        id1_mailbox.set_is_overlay_candidate(true);
        id1_mailbox.set_is_backed_by_surface_texture(true);
        ResourceId id1 = child_resource_provider_->CreateResourceFromTextureMailbox(
            id1_mailbox,
            SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback)));

        TextureMailbox id2_mailbox(external_mailbox, external_sync_token,
            GL_TEXTURE_EXTERNAL_OES);
        id2_mailbox.set_wants_promotion_hint(false);
        id2_mailbox.set_is_overlay_candidate(true);
        id2_mailbox.set_is_backed_by_surface_texture(false);
        ResourceId id2 = child_resource_provider_->CreateResourceFromTextureMailbox(
            id2_mailbox,
            SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback)));

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));

        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(2u, list.size());
            resource_provider_->ReceiveFromChild(child_id, list);
            {
                resource_provider_->WaitSyncTokenIfNeeded(list[0].id);
                ResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
                    list[0].id);
            }

            EXPECT_EQ(list[0].mailbox_holder.sync_token,
                context3d_->last_waited_sync_token());
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(2u, resource_provider_->num_resources());
        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        ResourceId mapped_id2 = resource_map[id2];
        EXPECT_NE(0u, mapped_id1);
        EXPECT_NE(0u, mapped_id2);

        // Make sure that the request for a promotion hint was noticed.
        EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id1));
        EXPECT_TRUE(resource_provider_->IsBackedBySurfaceTexture(mapped_id1));
        EXPECT_TRUE(resource_provider_->WantsPromotionHint(mapped_id1));

        EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id2));
        EXPECT_FALSE(resource_provider_->IsBackedBySurfaceTexture(mapped_id2));
        EXPECT_FALSE(resource_provider_->WantsPromotionHint(mapped_id2));

        EXPECT_EQ(1u, resource_provider_->CountPromotionHintRequestsForTesting());

        // ResourceProvider maintains a set of promotion hint requests that should be
        // cleared when resources are deleted.
        resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
        EXPECT_EQ(2u, returned_to_child.size());
        child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);

        EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());

        resource_provider_->DestroyChild(child_id);
    }
#endif

    class ResourceProviderTestNoSyncToken : public ResourceProviderTest {
    public:
        ResourceProviderTestNoSyncToken()
            : ResourceProviderTest(false)
        {
            EXPECT_EQ(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE, GetParam());
        }
    };

    TEST_P(ResourceProviderTestNoSyncToken, TransferGLResources)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        {
            // Ensure locking the memory buffer doesn't create an unnecessary sync
            // point.
            ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
                child_resource_provider_.get(), id2);
            EXPECT_TRUE(lock.GetGpuMemoryBuffer());
        }

        GLuint external_texture_id = child_context_->createExternalTexture();

        // A sync point is specified directly and should be used.
        gpu::Mailbox external_mailbox;
        child_context_->genMailboxCHROMIUM(external_mailbox.name);
        child_context_->produceTextureDirectCHROMIUM(
            external_texture_id, GL_TEXTURE_EXTERNAL_OES, external_mailbox.name);
        gpu::SyncToken external_sync_token;
        child_context_->genSyncToken(child_context_->insertFenceSync(),
            external_sync_token.GetData());
        EXPECT_TRUE(external_sync_token.HasData());
        ResourceId id3 = child_resource_provider_->CreateResourceFromTextureMailbox(
            TextureMailbox(external_mailbox, external_sync_token,
                GL_TEXTURE_EXTERNAL_OES),
            SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback)));

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        resource_provider_->SetChildNeedsSyncTokens(child_id, false);
        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);
            resource_ids_to_transfer.push_back(id3);
            TransferableResourceArray list;
            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(3u, list.size());
            // Standard resources shouldn't require creating and sending a sync point.
            EXPECT_FALSE(list[0].mailbox_holder.sync_token.HasData());
            EXPECT_FALSE(list[1].mailbox_holder.sync_token.HasData());
            // A given sync point should be passed through.
            EXPECT_EQ(external_sync_token, list[2].mailbox_holder.sync_token);
            resource_provider_->ReceiveFromChild(child_id, list);

            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_ids_to_receive.insert(id3);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            ASSERT_EQ(3u, returned_to_child.size());
            std::map<ResourceId, gpu::SyncToken> returned_sync_tokens;
            for (const auto& returned : returned_to_child)
                returned_sync_tokens[returned.id] = returned.sync_token;

            ASSERT_TRUE(returned_sync_tokens.find(id1) != returned_sync_tokens.end());
            // No new sync point should be created transferring back.
            ASSERT_TRUE(returned_sync_tokens.find(id1) != returned_sync_tokens.end());
            EXPECT_FALSE(returned_sync_tokens[id1].HasData());
            ASSERT_TRUE(returned_sync_tokens.find(id2) != returned_sync_tokens.end());
            EXPECT_FALSE(returned_sync_tokens[id2].HasData());
            // Original sync point given should be returned.
            ASSERT_TRUE(returned_sync_tokens.find(id3) != returned_sync_tokens.end());
            EXPECT_EQ(external_sync_token, returned_sync_tokens[id3]);
            EXPECT_FALSE(returned_to_child[0].lost);
            EXPECT_FALSE(returned_to_child[1].lost);
            EXPECT_FALSE(returned_to_child[2].lost);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }

        resource_provider_->DestroyChild(child_id);
    }

    INSTANTIATE_TEST_CASE_P(
        ResourceProviderTests,
        ResourceProviderTestNoSyncToken,
        ::testing::Values(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE));

    TEST_P(ResourceProviderTest, ReadLockCountStopsReturnToChildOrDelete)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(1u, list.size());
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));

            resource_provider_->ReceiveFromChild(child_id, list);

            resource_provider_->WaitSyncTokenIfNeeded(list[0].id);
            ResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
                list[0].id);

            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                ResourceIdSet());
            EXPECT_EQ(0u, returned_to_child.size());
        }

        EXPECT_EQ(1u, returned_to_child.size());
        child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);

        {
            child_resource_provider_->WaitSyncTokenIfNeeded(id1);
            ResourceProvider::ScopedReadLockGL lock(child_resource_provider_.get(),
                id1);
            child_resource_provider_->DeleteResource(id1);
            EXPECT_EQ(1u, child_resource_provider_->num_resources());
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
        }

        EXPECT_EQ(0u, child_resource_provider_->num_resources());
        resource_provider_->DestroyChild(child_id);
    }

    class TestFence : public ResourceProvider::Fence {
    public:
        TestFence() { }

        void Set() override { }
        bool HasPassed() override { return passed; }
        void Wait() override { }

        bool passed = false;

    private:
        ~TestFence() override { }
    };

    TEST_P(ResourceProviderTest, ReadLockFenceStopsReturnToChildOrDelete)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);
        child_resource_provider_->EnableReadLockFencesForTesting(id1);
        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));

        // Transfer some resources to the parent.
        ResourceProvider::ResourceIdArray resource_ids_to_transfer;
        resource_ids_to_transfer.push_back(id1);

        child_resource_provider_->GenerateSyncTokenForResources(
            resource_ids_to_transfer);

        TransferableResourceArray list;
        child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
            &list);
        ASSERT_EQ(1u, list.size());
        EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
        EXPECT_TRUE(list[0].read_lock_fences_enabled);

        resource_provider_->ReceiveFromChild(child_id, list);

        scoped_refptr<TestFence> fence(new TestFence);
        resource_provider_->SetReadLockFence(fence.get());
        {
            unsigned parent_id = list.front().id;
            resource_provider_->WaitSyncTokenIfNeeded(parent_id);
            ResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
                parent_id);
        }
        resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
        EXPECT_EQ(0u, returned_to_child.size());

        resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
        EXPECT_EQ(0u, returned_to_child.size());
        fence->passed = true;

        resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
        EXPECT_EQ(1u, returned_to_child.size());

        child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
        child_resource_provider_->DeleteResource(id1);
        EXPECT_EQ(0u, child_resource_provider_->num_resources());
    }

    TEST_P(ResourceProviderTest, ReadLockFenceDestroyChild)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data, size);
        child_resource_provider_->EnableReadLockFencesForTesting(id1);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        child_resource_provider_->CopyToResource(id2, data, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));

        // Transfer resources to the parent.
        ResourceProvider::ResourceIdArray resource_ids_to_transfer;
        resource_ids_to_transfer.push_back(id1);
        resource_ids_to_transfer.push_back(id2);

        child_resource_provider_->GenerateSyncTokenForResources(
            resource_ids_to_transfer);

        TransferableResourceArray list;
        child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
            &list);
        ASSERT_EQ(2u, list.size());
        EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
        EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));

        resource_provider_->ReceiveFromChild(child_id, list);

        scoped_refptr<TestFence> fence(new TestFence);
        resource_provider_->SetReadLockFence(fence.get());
        {
            for (size_t i = 0; i < list.size(); i++) {
                unsigned parent_id = list[i].id;
                resource_provider_->WaitSyncTokenIfNeeded(parent_id);
                ResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
                    parent_id);
            }
        }
        EXPECT_EQ(0u, returned_to_child.size());

        EXPECT_EQ(2u, resource_provider_->num_resources());

        resource_provider_->DestroyChild(child_id);

        EXPECT_EQ(0u, resource_provider_->num_resources());
        EXPECT_EQ(2u, returned_to_child.size());

        // id1 should be lost and id2 should not.
        EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
        EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);

        child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
        child_resource_provider_->DeleteResource(id1);
        child_resource_provider_->DeleteResource(id2);
        EXPECT_EQ(0u, child_resource_provider_->num_resources());
    }

    TEST_P(ResourceProviderTest, ReadLockFenceContextLost)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data, size);
        child_resource_provider_->EnableReadLockFencesForTesting(id1);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        child_resource_provider_->CopyToResource(id2, data, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));

        // Transfer resources to the parent.
        ResourceProvider::ResourceIdArray resource_ids_to_transfer;
        resource_ids_to_transfer.push_back(id1);
        resource_ids_to_transfer.push_back(id2);

        child_resource_provider_->GenerateSyncTokenForResources(
            resource_ids_to_transfer);

        TransferableResourceArray list;
        child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
            &list);
        ASSERT_EQ(2u, list.size());
        EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
        EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));

        resource_provider_->ReceiveFromChild(child_id, list);

        scoped_refptr<TestFence> fence(new TestFence);
        resource_provider_->SetReadLockFence(fence.get());
        {
            for (size_t i = 0; i < list.size(); i++) {
                unsigned parent_id = list[i].id;
                resource_provider_->WaitSyncTokenIfNeeded(parent_id);
                ResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
                    parent_id);
            }
        }
        EXPECT_EQ(0u, returned_to_child.size());

        EXPECT_EQ(2u, resource_provider_->num_resources());
        resource_provider_->DidLoseContextProvider();
        resource_provider_ = nullptr;

        EXPECT_EQ(2u, returned_to_child.size());

        EXPECT_TRUE(returned_to_child[0].lost);
        EXPECT_TRUE(returned_to_child[1].lost);
    }

    TEST_P(ResourceProviderTest, TransferSoftwareResources)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
            return;

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data2[4] = { 5, 5, 5, 5 };
        child_resource_provider_->CopyToResource(id2, data2, size);

        std::unique_ptr<SharedBitmap> shared_bitmap(CreateAndFillSharedBitmap(
            shared_bitmap_manager_.get(), gfx::Size(1, 1), 0));
        SharedBitmap* shared_bitmap_ptr = shared_bitmap.get();
        ResourceId id3 = child_resource_provider_->CreateResourceFromTextureMailbox(
            TextureMailbox(shared_bitmap_ptr, gfx::Size(1, 1)),
            SingleReleaseCallbackImpl::Create(base::Bind(
                &SharedBitmapReleaseCallback, base::Passed(&shared_bitmap))));

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);
            resource_ids_to_transfer.push_back(id3);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(3u, list.size());
            EXPECT_FALSE(list[0].mailbox_holder.sync_token.HasData());
            EXPECT_FALSE(list[1].mailbox_holder.sync_token.HasData());
            EXPECT_FALSE(list[2].mailbox_holder.sync_token.HasData());
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_ids_to_receive.insert(id3);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(3u, resource_provider_->num_resources());
        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        ResourceId mapped_id2 = resource_map[id2];
        ResourceId mapped_id3 = resource_map[id3];
        EXPECT_NE(0u, mapped_id1);
        EXPECT_NE(0u, mapped_id2);
        EXPECT_NE(0u, mapped_id3);
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id3));

        uint8_t result[4] = { 0 };
        GetResourcePixels(
            resource_provider_.get(), context(), mapped_id1, size, format, result);
        EXPECT_EQ(0, memcmp(data1, result, pixel_size));

        GetResourcePixels(
            resource_provider_.get(), context(), mapped_id2, size, format, result);
        EXPECT_EQ(0, memcmp(data2, result, pixel_size));

        {
            // Check that transfering again the same resource from the child to the
            // parent works.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_EQ(2u, list.size());
            EXPECT_EQ(id1, list[0].id);
            EXPECT_EQ(id2, list[1].id);
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            child_resource_provider_->ReceiveReturnsFromParent(returned);
            // ids were exported twice, we returned them only once, they should still
            // be in-use.
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
        }
        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            ASSERT_EQ(3u, returned_to_child.size());
            EXPECT_FALSE(returned_to_child[0].sync_token.HasData());
            EXPECT_FALSE(returned_to_child[1].sync_token.HasData());
            EXPECT_FALSE(returned_to_child[2].sync_token.HasData());
            std::set<ResourceId> expected_ids;
            expected_ids.insert(id1);
            expected_ids.insert(id2);
            expected_ids.insert(id3);
            std::set<ResourceId> returned_ids;
            for (unsigned i = 0; i < 3; i++)
                returned_ids.insert(returned_to_child[i].id);
            EXPECT_EQ(expected_ids, returned_ids);
            EXPECT_FALSE(returned_to_child[0].lost);
            EXPECT_FALSE(returned_to_child[1].lost);
            EXPECT_FALSE(returned_to_child[2].lost);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id1));
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id2));
        EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id3));

        {
            ResourceProvider::ScopedReadLockSoftware lock(
                child_resource_provider_.get(), id1);
            const SkBitmap* sk_bitmap = lock.sk_bitmap();
            EXPECT_EQ(sk_bitmap->width(), size.width());
            EXPECT_EQ(sk_bitmap->height(), size.height());
            EXPECT_EQ(0, memcmp(data1, sk_bitmap->getPixels(), pixel_size));
        }
        {
            ResourceProvider::ScopedReadLockSoftware lock(
                child_resource_provider_.get(), id2);
            const SkBitmap* sk_bitmap = lock.sk_bitmap();
            EXPECT_EQ(sk_bitmap->width(), size.width());
            EXPECT_EQ(sk_bitmap->height(), size.height());
            EXPECT_EQ(0, memcmp(data2, sk_bitmap->getPixels(), pixel_size));
        }
        {
            // Transfer resources to the parent again.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);
            resource_ids_to_transfer.push_back(id3);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(3u, list.size());
            EXPECT_EQ(id1, list[0].id);
            EXPECT_EQ(id2, list[1].id);
            EXPECT_EQ(id3, list[2].id);
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_ids_to_receive.insert(id3);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(0u, returned_to_child.size());

        EXPECT_EQ(3u, resource_provider_->num_resources());
        resource_provider_->DestroyChild(child_id);
        EXPECT_EQ(0u, resource_provider_->num_resources());

        ASSERT_EQ(3u, returned_to_child.size());
        EXPECT_FALSE(returned_to_child[0].sync_token.HasData());
        EXPECT_FALSE(returned_to_child[1].sync_token.HasData());
        EXPECT_FALSE(returned_to_child[2].sync_token.HasData());
        std::set<ResourceId> expected_ids;
        expected_ids.insert(id1);
        expected_ids.insert(id2);
        expected_ids.insert(id3);
        std::set<ResourceId> returned_ids;
        for (unsigned i = 0; i < 3; i++)
            returned_ids.insert(returned_to_child[i].id);
        EXPECT_EQ(expected_ids, returned_ids);
        EXPECT_FALSE(returned_to_child[0].lost);
        EXPECT_FALSE(returned_to_child[1].lost);
        EXPECT_FALSE(returned_to_child[2].lost);
    }

    TEST_P(ResourceProviderTest, TransferGLToSoftware)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
            return;

        scoped_refptr<TestContextProvider> child_context_provider = TestContextProvider::Create(
            ResourceProviderContext::Create(shared_data_.get()));
        child_context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> child_resource_provider(
            base::MakeUnique<ResourceProvider>(
                child_context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id1 = child_resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider->CopyToResource(id1, data1, size);
        child_resource_provider->GenerateSyncTokenForResource(id1);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);

            TransferableResourceArray list;
            child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(1u, list.size());
            EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
            EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
                list[0].mailbox_holder.texture_target);
            EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
            resource_provider_->ReceiveFromChild(child_id, list);
        }

        EXPECT_EQ(0u, resource_provider_->num_resources());
        ASSERT_EQ(1u, returned_to_child.size());
        EXPECT_EQ(returned_to_child[0].id, id1);
        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        EXPECT_EQ(0u, mapped_id1);

        resource_provider_->DestroyChild(child_id);
        EXPECT_EQ(0u, resource_provider_->num_resources());

        ASSERT_EQ(1u, returned_to_child.size());
        EXPECT_FALSE(returned_to_child[0].lost);
    }

    TEST_P(ResourceProviderTest, TransferInvalidSoftware)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
            return;

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(1u, list.size());
            // Make invalid.
            list[0].mailbox_holder.mailbox.name[1] ^= 0xff;
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            resource_provider_->ReceiveFromChild(child_id, list);
        }

        EXPECT_EQ(1u, resource_provider_->num_resources());
        EXPECT_EQ(0u, returned_to_child.size());

        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        EXPECT_NE(0u, mapped_id1);
        {
            ResourceProvider::ScopedReadLockSoftware lock(resource_provider_.get(),
                mapped_id1);
            EXPECT_FALSE(lock.valid());
        }

        resource_provider_->DestroyChild(child_id);
        EXPECT_EQ(0u, resource_provider_->num_resources());

        ASSERT_EQ(1u, returned_to_child.size());
        EXPECT_FALSE(returned_to_child[0].lost);
    }

    TEST_P(ResourceProviderTest, DeleteExportedResources)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data2[4] = { 5, 5, 5, 5 };
        child_resource_provider_->CopyToResource(id2, data2, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(2u, list.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
                EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
            }
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(2u, resource_provider_->num_resources());
        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        ResourceId mapped_id2 = resource_map[id2];
        EXPECT_NE(0u, mapped_id1);
        EXPECT_NE(0u, mapped_id2);
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));

        {
            // The parent transfers the resources to the grandparent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(mapped_id1);
            resource_ids_to_transfer.push_back(mapped_id2);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);

            ASSERT_EQ(2u, list.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
                EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
            }
            EXPECT_TRUE(resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(resource_provider_->InUseByConsumer(id2));

            // Release the resource in the parent. Set no resources as being in use. The
            // resources are exported so that can't be transferred back yet.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            EXPECT_EQ(0u, returned_to_child.size());
            EXPECT_EQ(2u, resource_provider_->num_resources());

            // Return the resources from the grandparent to the parent. They should be
            // returned to the child then.
            EXPECT_EQ(2u, list.size());
            EXPECT_EQ(mapped_id1, list[0].id);
            EXPECT_EQ(mapped_id2, list[1].id);
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            resource_provider_->ReceiveReturnsFromParent(returned);

            EXPECT_EQ(0u, resource_provider_->num_resources());
            ASSERT_EQ(2u, returned_to_child.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
                EXPECT_TRUE(returned_to_child[1].sync_token.HasData());
            }
            EXPECT_FALSE(returned_to_child[0].lost);
            EXPECT_FALSE(returned_to_child[1].lost);
        }
    }

    TEST_P(ResourceProviderTest, DestroyChildWithExportedResources)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id1 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data1[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id1, data1, size);

        ResourceId id2 = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data2[4] = { 5, 5, 5, 5 };
        child_resource_provider_->CopyToResource(id2, data2, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer some resources to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id1);
            resource_ids_to_transfer.push_back(id2);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(2u, list.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
                EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
            }
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id1);
            resource_ids_to_receive.insert(id2);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        EXPECT_EQ(2u, resource_provider_->num_resources());
        ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
        ResourceId mapped_id1 = resource_map[id1];
        ResourceId mapped_id2 = resource_map[id2];
        EXPECT_NE(0u, mapped_id1);
        EXPECT_NE(0u, mapped_id2);
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
        EXPECT_FALSE(resource_provider_->InUseByConsumer(id2));

        {
            // The parent transfers the resources to the grandparent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(mapped_id1);
            resource_ids_to_transfer.push_back(mapped_id2);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);

            ASSERT_EQ(2u, list.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
                EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
            }
            EXPECT_TRUE(resource_provider_->InUseByConsumer(id1));
            EXPECT_TRUE(resource_provider_->InUseByConsumer(id2));

            // Release the resource in the parent. Set no resources as being in use. The
            // resources are exported so that can't be transferred back yet.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            // Destroy the child, the resources should not be returned yet.
            EXPECT_EQ(0u, returned_to_child.size());
            EXPECT_EQ(2u, resource_provider_->num_resources());

            resource_provider_->DestroyChild(child_id);

            EXPECT_EQ(2u, resource_provider_->num_resources());
            ASSERT_EQ(0u, returned_to_child.size());

            // Return a resource from the grandparent, it should be returned at this
            // point.
            EXPECT_EQ(2u, list.size());
            EXPECT_EQ(mapped_id1, list[0].id);
            EXPECT_EQ(mapped_id2, list[1].id);
            TransferableResourceArray return_list;
            return_list.push_back(list[1]);
            list.pop_back();
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(return_list, &returned);
            resource_provider_->ReceiveReturnsFromParent(returned);

            EXPECT_EQ(1u, resource_provider_->num_resources());
            ASSERT_EQ(1u, returned_to_child.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
            }
            EXPECT_FALSE(returned_to_child[0].lost);
            returned_to_child.clear();

            // Destroy the parent resource provider. The resource that's left should be
            // lost at this point, and returned.
            resource_provider_ = nullptr;
            ASSERT_EQ(1u, returned_to_child.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
                EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
            }
            EXPECT_TRUE(returned_to_child[0].lost);
        }
    }

    TEST_P(ResourceProviderTest, DeleteTransferredResources)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id, data, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer some resource to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            ASSERT_EQ(1u, list.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
                EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        // Delete textures in the child, while they are transfered.
        child_resource_provider_->DeleteResource(id);
        EXPECT_EQ(1u, child_resource_provider_->num_resources());
        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            ASSERT_EQ(1u, returned_to_child.size());
            if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
                EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
        }
        EXPECT_EQ(0u, child_resource_provider_->num_resources());
    }

    TEST_P(ResourceProviderTest, UnuseTransferredResources)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        size_t pixel_size = TextureSizeBytes(size, format);
        ASSERT_EQ(4U, pixel_size);

        ResourceId id = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        uint8_t data[4] = { 1, 2, 3, 4 };
        child_resource_provider_->CopyToResource(id, data, size);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        const ResourceProvider::ResourceIdMap& map = resource_provider_->GetChildToParentMap(child_id);
        {
            // Transfer some resource to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id);

            child_resource_provider_->GenerateSyncTokenForResources(
                resource_ids_to_transfer);

            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }
        TransferableResourceArray sent_to_top_level;
        {
            // Parent transfers to top-level.
            ASSERT_TRUE(map.find(id) != map.end());
            ResourceId parent_id = map.find(id)->second;
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(parent_id);
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &sent_to_top_level);
            EXPECT_TRUE(resource_provider_->InUseByConsumer(parent_id));
        }
        {
            // Stop using resource.
            ResourceIdSet empty;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, empty);
            // Resource is not yet returned to the child, since it's in use by the
            // top-level.
            EXPECT_TRUE(returned_to_child.empty());
        }
        {
            // Send the resource to the parent again.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(id);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(id);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }
        {
            // Receive returns back from top-level.
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(sent_to_top_level, &returned);
            resource_provider_->ReceiveReturnsFromParent(returned);
            // Resource is still not yet returned to the child, since it's declared used
            // in the parent.
            EXPECT_TRUE(returned_to_child.empty());
            ASSERT_TRUE(map.find(id) != map.end());
            ResourceId parent_id = map.find(id)->second;
            EXPECT_FALSE(resource_provider_->InUseByConsumer(parent_id));
        }
        {
            sent_to_top_level.clear();
            // Parent transfers again to top-level.
            ASSERT_TRUE(map.find(id) != map.end());
            ResourceId parent_id = map.find(id)->second;
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(parent_id);
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &sent_to_top_level);
            EXPECT_TRUE(resource_provider_->InUseByConsumer(parent_id));
        }
        {
            // Receive returns back from top-level.
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(sent_to_top_level, &returned);
            resource_provider_->ReceiveReturnsFromParent(returned);
            // Resource is still not yet returned to the child, since it's still
            // declared used in the parent.
            EXPECT_TRUE(returned_to_child.empty());
            ASSERT_TRUE(map.find(id) != map.end());
            ResourceId parent_id = map.find(id)->second;
            EXPECT_FALSE(resource_provider_->InUseByConsumer(parent_id));
        }
        {
            // Stop using resource.
            ResourceIdSet empty;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, empty);
            // Resource should have been returned to the child, since it's no longer in
            // use by the top-level.
            ASSERT_EQ(1u, returned_to_child.size());
            EXPECT_EQ(id, returned_to_child[0].id);
            EXPECT_EQ(2, returned_to_child[0].count);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
            EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id));
        }
    }

    class ResourceProviderTestTextureFilters : public ResourceProviderTest {
    public:
        static void RunTest(GLenum child_filter, GLenum parent_filter)
        {
            std::unique_ptr<TextureStateTrackingContext> child_context_owned(
                new TextureStateTrackingContext);
            TextureStateTrackingContext* child_context = child_context_owned.get();

            auto child_context_provider = TestContextProvider::Create(std::move(child_context_owned));
            child_context_provider->BindToCurrentThread();
            auto shared_bitmap_manager = base::MakeUnique<TestSharedBitmapManager>();

            std::unique_ptr<ResourceProvider> child_resource_provider(
                base::MakeUnique<ResourceProvider>(
                    child_context_provider.get(), shared_bitmap_manager.get(), nullptr,
                    nullptr, 0, 1, kDelegatedSyncPointsRequired,
                    kUseGpuMemoryBufferResources, kEnableColorCorrectRendering,
                    DefaultBufferToTextureTargetMapForTesting()));

            std::unique_ptr<TextureStateTrackingContext> parent_context_owned(
                new TextureStateTrackingContext);
            TextureStateTrackingContext* parent_context = parent_context_owned.get();

            auto parent_context_provider = TestContextProvider::Create(std::move(parent_context_owned));
            parent_context_provider->BindToCurrentThread();

            std::unique_ptr<ResourceProvider> parent_resource_provider(
                base::MakeUnique<ResourceProvider>(
                    parent_context_provider.get(), shared_bitmap_manager.get(), nullptr,
                    nullptr, 0, 1, kDelegatedSyncPointsRequired,
                    kUseGpuMemoryBufferResources, kEnableColorCorrectRendering,
                    DefaultBufferToTextureTargetMapForTesting()));

            gfx::Size size(1, 1);
            ResourceFormat format = RGBA_8888;
            int child_texture_id = 1;
            int parent_texture_id = 2;

            size_t pixel_size = TextureSizeBytes(size, format);
            ASSERT_EQ(4U, pixel_size);

            ResourceId id = child_resource_provider->CreateResource(
                size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
                gfx::ColorSpace());

            // The new texture is created with GL_LINEAR.
            EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id))
                .Times(2); // Once to create and once to allocate.
            EXPECT_CALL(*child_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            EXPECT_CALL(*child_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            EXPECT_CALL(
                *child_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
            EXPECT_CALL(
                *child_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
            child_resource_provider->AllocateForTesting(id);
            Mock::VerifyAndClearExpectations(child_context);

            uint8_t data[4] = { 1, 2, 3, 4 };

            EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id));
            child_resource_provider->CopyToResource(id, data, size);
            Mock::VerifyAndClearExpectations(child_context);

            // The texture is set to |child_filter| in the child.
            EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id));
            if (child_filter != GL_LINEAR) {
                EXPECT_CALL(
                    *child_context,
                    texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, child_filter));
                EXPECT_CALL(
                    *child_context,
                    texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, child_filter));
            }
            SetResourceFilter(child_resource_provider.get(), id, child_filter);
            Mock::VerifyAndClearExpectations(child_context);

            ReturnedResourceArray returned_to_child;
            int child_id = parent_resource_provider->CreateChild(
                GetReturnCallback(&returned_to_child));
            {
                // Transfer some resource to the parent.
                ResourceProvider::ResourceIdArray resource_ids_to_transfer;
                resource_ids_to_transfer.push_back(id);
                TransferableResourceArray list;

                EXPECT_CALL(*child_context,
                    produceTextureDirectCHROMIUM(_, GL_TEXTURE_2D, _));

                child_resource_provider->GenerateSyncTokenForResources(
                    resource_ids_to_transfer);
                child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
                    &list);
                Mock::VerifyAndClearExpectations(child_context);

                ASSERT_EQ(1u, list.size());
                EXPECT_EQ(static_cast<unsigned>(child_filter), list[0].filter);

                EXPECT_CALL(*parent_context,
                    createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, _))
                    .WillOnce(Return(parent_texture_id));

                parent_resource_provider->ReceiveFromChild(child_id, list);
                {
                    parent_resource_provider->WaitSyncTokenIfNeeded(list[0].id);
                    ResourceProvider::ScopedReadLockGL lock(parent_resource_provider.get(),
                        list[0].id);
                }
                Mock::VerifyAndClearExpectations(parent_context);

                ResourceIdSet resource_ids_to_receive;
                resource_ids_to_receive.insert(id);
                parent_resource_provider->DeclareUsedResourcesFromChild(
                    child_id, resource_ids_to_receive);
                Mock::VerifyAndClearExpectations(parent_context);
            }
            ResourceProvider::ResourceIdMap resource_map = parent_resource_provider->GetChildToParentMap(child_id);
            ResourceId mapped_id = resource_map[id];
            EXPECT_NE(0u, mapped_id);

            // The texture is set to |parent_filter| in the parent.
            EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
            EXPECT_CALL(
                *parent_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, parent_filter));
            EXPECT_CALL(
                *parent_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, parent_filter));
            SetResourceFilter(parent_resource_provider.get(), mapped_id, parent_filter);
            Mock::VerifyAndClearExpectations(parent_context);

            // The texture should be reset to |child_filter| in the parent when it is
            // returned, since that is how it was received.
            EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
            EXPECT_CALL(
                *parent_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, child_filter));
            EXPECT_CALL(
                *parent_context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, child_filter));

            {
                EXPECT_EQ(0u, returned_to_child.size());

                // Transfer resources back from the parent to the child. Set no resources
                // as being in use.
                ResourceIdSet no_resources;
                parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
                    no_resources);
                Mock::VerifyAndClearExpectations(parent_context);

                ASSERT_EQ(1u, returned_to_child.size());
                child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
            }

            // The child remembers the texture filter is set to |child_filter|.
            EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id));
            SetResourceFilter(child_resource_provider.get(), id, child_filter);
            Mock::VerifyAndClearExpectations(child_context);
        }
    };

    TEST_P(ResourceProviderTest, TextureFilters_ChildNearestParentLinear)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        ResourceProviderTestTextureFilters::RunTest(GL_NEAREST, GL_LINEAR);
    }

    TEST_P(ResourceProviderTest, TextureFilters_ChildLinearParentNearest)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        ResourceProviderTestTextureFilters::RunTest(GL_LINEAR, GL_NEAREST);
    }

    TEST_P(ResourceProviderTest, TransferMailboxResources)
    {
        // Other mailbox transfers tested elsewhere.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        unsigned texture = context()->createTexture();
        context()->bindTexture(GL_TEXTURE_2D, texture);
        uint8_t data[4] = { 1, 2, 3, 4 };
        context()->texImage2D(
            GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data);
        gpu::Mailbox mailbox;
        context()->genMailboxCHROMIUM(mailbox.name);
        context()->produceTextureDirectCHROMIUM(texture, GL_TEXTURE_2D, mailbox.name);
        gpu::SyncToken sync_token;
        context()->genSyncToken(context()->insertFenceSync(), sync_token.GetData());
        EXPECT_TRUE(sync_token.HasData());

        // All the logic below assumes that the sync token releases are all positive.
        EXPECT_LT(0u, sync_token.release_count());

        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        BlockingTaskRunner* main_thread_task_runner = nullptr;
        ReleaseCallbackImpl callback = base::Bind(ReleaseCallback, &release_sync_token, &lost_resource,
            &main_thread_task_runner);
        ResourceId resource = resource_provider_->CreateResourceFromTextureMailbox(
            TextureMailbox(mailbox, sync_token, GL_TEXTURE_2D),
            SingleReleaseCallbackImpl::Create(callback));
        EXPECT_EQ(1u, context()->NumTextures());
        EXPECT_FALSE(release_sync_token.HasData());
        {
            // Transfer the resource, expect the sync points to be consistent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(resource);
            TransferableResourceArray list;
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
            ASSERT_EQ(1u, list.size());
            EXPECT_LE(sync_token.release_count(),
                list[0].mailbox_holder.sync_token.release_count());
            EXPECT_EQ(0,
                memcmp(mailbox.name,
                    list[0].mailbox_holder.mailbox.name,
                    sizeof(mailbox.name)));
            EXPECT_FALSE(release_sync_token.HasData());

            context()->waitSyncToken(list[0].mailbox_holder.sync_token.GetConstData());
            unsigned other_texture = context()->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
            uint8_t test_data[4] = { 0 };
            context()->GetPixels(
                gfx::Size(1, 1), RGBA_8888, test_data);
            EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));

            context()->produceTextureDirectCHROMIUM(other_texture, GL_TEXTURE_2D,
                mailbox.name);
            context()->deleteTexture(other_texture);
            context()->genSyncToken(context()->insertFenceSync(),
                list[0].mailbox_holder.sync_token.GetData());
            EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());

            // Receive the resource, then delete it, expect the sync points to be
            // consistent.
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            resource_provider_->ReceiveReturnsFromParent(returned);
            EXPECT_EQ(1u, context()->NumTextures());
            EXPECT_FALSE(release_sync_token.HasData());

            resource_provider_->DeleteResource(resource);
            EXPECT_LE(list[0].mailbox_holder.sync_token.release_count(),
                release_sync_token.release_count());
            EXPECT_FALSE(lost_resource);
            EXPECT_EQ(main_thread_task_runner_.get(), main_thread_task_runner);
        }

        // We're going to do the same thing as above, but testing the case where we
        // delete the resource before we receive it back.
        sync_token = release_sync_token;
        EXPECT_LT(0u, sync_token.release_count());
        release_sync_token.Clear();
        resource = resource_provider_->CreateResourceFromTextureMailbox(
            TextureMailbox(mailbox, sync_token, GL_TEXTURE_2D),
            SingleReleaseCallbackImpl::Create(callback));
        EXPECT_EQ(1u, context()->NumTextures());
        EXPECT_FALSE(release_sync_token.HasData());
        {
            // Transfer the resource, expect the sync points to be consistent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(resource);
            TransferableResourceArray list;
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
            ASSERT_EQ(1u, list.size());
            EXPECT_LE(sync_token.release_count(),
                list[0].mailbox_holder.sync_token.release_count());
            EXPECT_EQ(0,
                memcmp(mailbox.name,
                    list[0].mailbox_holder.mailbox.name,
                    sizeof(mailbox.name)));
            EXPECT_FALSE(release_sync_token.HasData());

            context()->waitSyncToken(list[0].mailbox_holder.sync_token.GetConstData());
            unsigned other_texture = context()->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
            uint8_t test_data[4] = { 0 };
            context()->GetPixels(
                gfx::Size(1, 1), RGBA_8888, test_data);
            EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));

            context()->produceTextureDirectCHROMIUM(other_texture, GL_TEXTURE_2D,
                mailbox.name);
            context()->deleteTexture(other_texture);
            context()->genSyncToken(context()->insertFenceSync(),
                list[0].mailbox_holder.sync_token.GetData());
            EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());

            // Delete the resource, which shouldn't do anything.
            resource_provider_->DeleteResource(resource);
            EXPECT_EQ(1u, context()->NumTextures());
            EXPECT_FALSE(release_sync_token.HasData());

            // Then receive the resource which should release the mailbox, expect the
            // sync points to be consistent.
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            resource_provider_->ReceiveReturnsFromParent(returned);
            EXPECT_LE(list[0].mailbox_holder.sync_token.release_count(),
                release_sync_token.release_count());
            EXPECT_FALSE(lost_resource);
            EXPECT_EQ(main_thread_task_runner_.get(), main_thread_task_runner);
        }

        context()->waitSyncToken(release_sync_token.GetConstData());
        texture = context()->createAndConsumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
        context()->deleteTexture(texture);
    }

    TEST_P(ResourceProviderTest, LostResourceInParent)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        ResourceId resource = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        child_resource_provider_->AllocateForTesting(resource);
        // Expect a GL resource to be lost.
        bool should_lose_resource = GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE;

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer the resource to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(resource);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_EQ(1u, list.size());

            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(resource);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        // Lose the output surface in the parent.
        resource_provider_->DidLoseContextProvider();

        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            // Expect a GL resource to be lost.
            ASSERT_EQ(1u, returned_to_child.size());
            EXPECT_EQ(should_lose_resource, returned_to_child[0].lost);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }

        // A GL resource should be lost.
        EXPECT_EQ(should_lose_resource, child_resource_provider_->IsLost(resource));

        // Lost resources stay in use in the parent forever.
        EXPECT_EQ(should_lose_resource,
            child_resource_provider_->InUseByConsumer(resource));
    }

    TEST_P(ResourceProviderTest, LostResourceInGrandParent)
    {
        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        ResourceId resource = child_resource_provider_->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        child_resource_provider_->AllocateForTesting(resource);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer the resource to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(resource);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_EQ(1u, list.size());

            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(resource);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        {
            ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
            ResourceId parent_resource = resource_map[resource];
            EXPECT_NE(0u, parent_resource);

            // Transfer to a grandparent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(parent_resource);
            TransferableResourceArray list;
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);

            // Receive back a lost resource from the grandparent.
            EXPECT_EQ(1u, list.size());
            EXPECT_EQ(parent_resource, list[0].id);
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            EXPECT_EQ(1u, returned.size());
            EXPECT_EQ(parent_resource, returned[0].id);
            returned[0].lost = true;
            resource_provider_->ReceiveReturnsFromParent(returned);

            // The resource should be lost.
            EXPECT_TRUE(resource_provider_->IsLost(parent_resource));

            // Lost resources stay in use in the parent forever.
            EXPECT_TRUE(resource_provider_->InUseByConsumer(parent_resource));
        }

        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            // Expect the resource to be lost.
            ASSERT_EQ(1u, returned_to_child.size());
            EXPECT_TRUE(returned_to_child[0].lost);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }

        // The resource should be lost.
        EXPECT_TRUE(child_resource_provider_->IsLost(resource));

        // Lost resources stay in use in the parent forever.
        EXPECT_TRUE(child_resource_provider_->InUseByConsumer(resource));
    }

    TEST_P(ResourceProviderTest, LostMailboxInParent)
    {
        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        bool release_called = false;
        gpu::SyncToken sync_token;
        ResourceId resource = CreateChildMailbox(&release_sync_token, &lost_resource,
            &release_called, &sync_token);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer the resource to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(resource);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_EQ(1u, list.size());

            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(resource);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        // Lose the output surface in the parent.
        resource_provider_->DidLoseContextProvider();

        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            ASSERT_EQ(1u, returned_to_child.size());
            // Losing an output surface only loses hardware resources.
            EXPECT_EQ(returned_to_child[0].lost,
                GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }

        // Delete the resource in the child. Expect the resource to be lost if it's
        // a GL texture.
        child_resource_provider_->DeleteResource(resource);
        EXPECT_EQ(lost_resource,
            GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE);
    }

    TEST_P(ResourceProviderTest, LostMailboxInGrandParent)
    {
        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        bool release_called = false;
        gpu::SyncToken sync_token;
        ResourceId resource = CreateChildMailbox(&release_sync_token, &lost_resource,
            &release_called, &sync_token);

        ReturnedResourceArray returned_to_child;
        int child_id = resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
        {
            // Transfer the resource to the parent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(resource);
            TransferableResourceArray list;
            child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
                &list);
            EXPECT_EQ(1u, list.size());

            resource_provider_->ReceiveFromChild(child_id, list);
            ResourceIdSet resource_ids_to_receive;
            resource_ids_to_receive.insert(resource);
            resource_provider_->DeclareUsedResourcesFromChild(child_id,
                resource_ids_to_receive);
        }

        {
            ResourceProvider::ResourceIdMap resource_map = resource_provider_->GetChildToParentMap(child_id);
            ResourceId parent_resource = resource_map[resource];
            EXPECT_NE(0u, parent_resource);

            // Transfer to a grandparent.
            ResourceProvider::ResourceIdArray resource_ids_to_transfer;
            resource_ids_to_transfer.push_back(parent_resource);
            TransferableResourceArray list;
            resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);

            // Receive back a lost resource from the grandparent.
            EXPECT_EQ(1u, list.size());
            EXPECT_EQ(parent_resource, list[0].id);
            ReturnedResourceArray returned;
            TransferableResource::ReturnResources(list, &returned);
            EXPECT_EQ(1u, returned.size());
            EXPECT_EQ(parent_resource, returned[0].id);
            returned[0].lost = true;
            resource_provider_->ReceiveReturnsFromParent(returned);
        }

        {
            EXPECT_EQ(0u, returned_to_child.size());

            // Transfer resources back from the parent to the child. Set no resources as
            // being in use.
            ResourceIdSet no_resources;
            resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);

            // Expect the resource to be lost.
            ASSERT_EQ(1u, returned_to_child.size());
            EXPECT_TRUE(returned_to_child[0].lost);
            child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
            returned_to_child.clear();
        }

        // Delete the resource in the child. Expect the resource to be lost.
        child_resource_provider_->DeleteResource(resource);
        EXPECT_TRUE(lost_resource);
    }

    TEST_P(ResourceProviderTest, Shutdown)
    {
        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        bool release_called = false;
        gpu::SyncToken sync_token;
        CreateChildMailbox(&release_sync_token, &lost_resource, &release_called,
            &sync_token);

        EXPECT_FALSE(release_sync_token.HasData());
        EXPECT_FALSE(lost_resource);

        child_resource_provider_ = nullptr;

        if (GetParam() == ResourceProvider::RESOURCE_TYPE_GL_TEXTURE) {
            EXPECT_LE(sync_token.release_count(), release_sync_token.release_count());
        }
        EXPECT_TRUE(release_called);
        EXPECT_FALSE(lost_resource);
    }

    TEST_P(ResourceProviderTest, ShutdownWithExportedResource)
    {
        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        bool release_called = false;
        gpu::SyncToken sync_token;
        ResourceId resource = CreateChildMailbox(&release_sync_token, &lost_resource,
            &release_called, &sync_token);

        // Transfer the resource, so we can't release it properly on shutdown.
        ResourceProvider::ResourceIdArray resource_ids_to_transfer;
        resource_ids_to_transfer.push_back(resource);
        TransferableResourceArray list;
        child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
            &list);

        EXPECT_FALSE(release_sync_token.HasData());
        EXPECT_FALSE(lost_resource);

        child_resource_provider_ = nullptr;

        // Since the resource is in the parent, the child considers it lost.
        EXPECT_TRUE(lost_resource);
    }

    TEST_P(ResourceProviderTest, LostContext)
    {
        // TextureMailbox callbacks only exist for GL textures for now.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        unsigned texture = context()->createTexture();
        context()->bindTexture(GL_TEXTURE_2D, texture);
        gpu::Mailbox mailbox;
        context()->genMailboxCHROMIUM(mailbox.name);
        context()->produceTextureDirectCHROMIUM(texture, GL_TEXTURE_2D, mailbox.name);
        gpu::SyncToken sync_token;
        context()->genSyncToken(context()->insertFenceSync(), sync_token.GetData());

        EXPECT_TRUE(sync_token.HasData());

        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        BlockingTaskRunner* main_thread_task_runner = nullptr;
        std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(
            base::Bind(ReleaseCallback, &release_sync_token, &lost_resource,
                &main_thread_task_runner));
        resource_provider_->CreateResourceFromTextureMailbox(
            TextureMailbox(mailbox, sync_token, GL_TEXTURE_2D), std::move(callback));

        EXPECT_FALSE(release_sync_token.HasData());
        EXPECT_FALSE(lost_resource);
        EXPECT_FALSE(main_thread_task_runner);

        resource_provider_->DidLoseContextProvider();
        resource_provider_ = nullptr;

        EXPECT_LE(sync_token.release_count(), release_sync_token.release_count());
        EXPECT_TRUE(lost_resource);
        EXPECT_EQ(main_thread_task_runner_.get(), main_thread_task_runner);
    }

    TEST_P(ResourceProviderTest, ScopedSampler)
    {
        // Sampling is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        int texture_id = 1;

        ResourceId id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());

        // Check that the texture gets created with the right sampler settings.
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id))
            .Times(2); // Once to create and once to allocate.
        EXPECT_CALL(*context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
        EXPECT_CALL(*context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
        EXPECT_CALL(
            *context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
        EXPECT_CALL(
            *context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));

        resource_provider->AllocateForTesting(id);
        Mock::VerifyAndClearExpectations(context);

        // Creating a sampler with the default filter should not change any texture
        // parameters.
        {
            EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
            ResourceProvider::ScopedSamplerGL sampler(
                resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
            Mock::VerifyAndClearExpectations(context);
        }

        // Using a different filter should be reflected in the texture parameters.
        {
            EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
            EXPECT_CALL(
                *context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
            EXPECT_CALL(
                *context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
            ResourceProvider::ScopedSamplerGL sampler(
                resource_provider.get(), id, GL_TEXTURE_2D, GL_NEAREST);
            Mock::VerifyAndClearExpectations(context);
        }

        // Test resetting to the default filter.
        {
            EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            ResourceProvider::ScopedSamplerGL sampler(
                resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
            Mock::VerifyAndClearExpectations(context);
        }
    }

    TEST_P(ResourceProviderTest, ManagedResource)
    {
        // Sampling is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;
        int texture_id = 1;

        // Check that the texture gets created with the right sampler settings.
        ResourceId id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
        EXPECT_CALL(*context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
        EXPECT_CALL(*context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
        EXPECT_CALL(
            *context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
        EXPECT_CALL(
            *context,
            texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
        resource_provider->CreateForTesting(id);
        EXPECT_NE(0u, id);

        Mock::VerifyAndClearExpectations(context);
    }

    TEST_P(ResourceProviderTest, TextureWrapMode)
    {
        // Sampling is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        for (int texture_id = 1; texture_id <= 2; ++texture_id) {
            // Check that the texture gets created with the right sampler settings.
            ResourceId id = resource_provider->CreateResource(
                size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
                gfx::ColorSpace());
            EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
            EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
            resource_provider->CreateForTesting(id);
            EXPECT_NE(0u, id);

            Mock::VerifyAndClearExpectations(context);
        }
    }

    TEST_P(ResourceProviderTest, TextureHint)
    {
        // Sampling is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        context->set_support_texture_storage(true);
        context->set_support_texture_usage(true);
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        const ResourceProvider::TextureHint hints[4] = {
            ResourceProvider::TEXTURE_HINT_DEFAULT,
            ResourceProvider::TEXTURE_HINT_IMMUTABLE,
            ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
            ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
        };
        for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
            // Check that the texture gets created with the right sampler settings.
            ResourceId id = resource_provider->CreateResource(
                size, hints[texture_id - 1], format, gfx::ColorSpace());
            EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            EXPECT_CALL(
                *context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
            EXPECT_CALL(
                *context,
                texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
            // Check only TEXTURE_HINT_FRAMEBUFFER set GL_TEXTURE_USAGE_ANGLE.
            bool is_framebuffer_hint = hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_FRAMEBUFFER;
            EXPECT_CALL(*context,
                texParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_USAGE_ANGLE,
                    GL_FRAMEBUFFER_ATTACHMENT_ANGLE))
                .Times(is_framebuffer_hint ? 1 : 0);
            resource_provider->CreateForTesting(id);
            EXPECT_NE(0u, id);

            Mock::VerifyAndClearExpectations(context);
        }
    }

    TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_BITMAP)
            return;

        gfx::Size size(64, 64);
        const uint32_t kBadBeef = 0xbadbeef;
        std::unique_ptr<SharedBitmap> shared_bitmap(
            CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, kBadBeef));

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                nullptr, shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), main_thread_task_runner_.get(), 0,
                1, kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gpu::SyncToken release_sync_token;
        bool lost_resource = false;
        BlockingTaskRunner* main_thread_task_runner = nullptr;
        std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(
            base::Bind(&ReleaseCallback, &release_sync_token, &lost_resource,
                &main_thread_task_runner));
        TextureMailbox mailbox(shared_bitmap.get(), size);

        ResourceId id = resource_provider->CreateResourceFromTextureMailbox(
            mailbox, std::move(callback));
        EXPECT_NE(0u, id);

        {
            ResourceProvider::ScopedReadLockSoftware lock(resource_provider.get(), id);
            const SkBitmap* sk_bitmap = lock.sk_bitmap();
            EXPECT_EQ(sk_bitmap->width(), size.width());
            EXPECT_EQ(sk_bitmap->height(), size.height());
            EXPECT_EQ(*sk_bitmap->getAddr32(16, 16), kBadBeef);
        }

        resource_provider->DeleteResource(id);
        EXPECT_FALSE(release_sync_token.HasData());
        EXPECT_FALSE(lost_resource);
        EXPECT_EQ(main_thread_task_runner_.get(), main_thread_task_runner);
    }

    class ResourceProviderTestTextureMailboxGLFilters
        : public ResourceProviderTest {
    public:
        static void RunTest(TestSharedBitmapManager* shared_bitmap_manager,
            TestGpuMemoryBufferManager* gpu_memory_buffer_manager,
            BlockingTaskRunner* main_thread_task_runner,
            bool mailbox_nearest_neighbor,
            GLenum sampler_filter)
        {
            std::unique_ptr<TextureStateTrackingContext> context_owned(
                new TextureStateTrackingContext);
            TextureStateTrackingContext* context = context_owned.get();
            auto context_provider = TestContextProvider::Create(std::move(context_owned));
            context_provider->BindToCurrentThread();

            std::unique_ptr<ResourceProvider> resource_provider(
                base::MakeUnique<ResourceProvider>(
                    context_provider.get(), shared_bitmap_manager,
                    gpu_memory_buffer_manager, main_thread_task_runner, 0, 1,
                    kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                    kEnableColorCorrectRendering,
                    DefaultBufferToTextureTargetMapForTesting()));

            unsigned texture_id = 1;
            gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0,
                gpu::CommandBufferId::FromUnsafeValue(0x12),
                0x34);
            unsigned target = GL_TEXTURE_2D;
            const GLuint64 current_fence_sync = context->GetNextFenceSync();

            EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
            EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
            EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);
            EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_, _)).Times(0);

            gpu::Mailbox gpu_mailbox;
            memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
            gpu::SyncToken release_sync_token;
            bool lost_resource = false;
            BlockingTaskRunner* mailbox_task_runner = nullptr;
            std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(
                base::Bind(&ReleaseCallback, &release_sync_token, &lost_resource,
                    &mailbox_task_runner));

            TextureMailbox mailbox(gpu_mailbox, sync_token, target);
            mailbox.set_nearest_neighbor(mailbox_nearest_neighbor);

            ResourceId id = resource_provider->CreateResourceFromTextureMailbox(
                mailbox, std::move(callback));
            EXPECT_NE(0u, id);
            EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());

            Mock::VerifyAndClearExpectations(context);

            {
                // Mailbox sync point WaitSyncToken before using the texture.
                EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
                resource_provider->WaitSyncTokenIfNeeded(id);
                Mock::VerifyAndClearExpectations(context);

                EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(target, _))
                    .WillOnce(Return(texture_id));
                EXPECT_CALL(*context, bindTexture(target, texture_id));

                EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);

                // The sampler will reset these if |mailbox_nearest_neighbor| does not
                // match |sampler_filter|.
                if (mailbox_nearest_neighbor != (sampler_filter == GL_NEAREST)) {
                    EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, sampler_filter));
                    EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sampler_filter));
                }

                ResourceProvider::ScopedSamplerGL lock(
                    resource_provider.get(), id, sampler_filter);
                Mock::VerifyAndClearExpectations(context);
                EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());

                // When done with it, a sync point should be inserted, but no produce is
                // necessary.
                EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
                EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);

                EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
                EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_, _)).Times(0);
            }

            resource_provider->DeleteResource(id);
            EXPECT_TRUE(release_sync_token.HasData());
            EXPECT_FALSE(lost_resource);
            EXPECT_EQ(main_thread_task_runner, mailbox_task_runner);
        }
    };

    TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_LinearToLinear)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        ResourceProviderTestTextureMailboxGLFilters::RunTest(
            shared_bitmap_manager_.get(),
            gpu_memory_buffer_manager_.get(),
            main_thread_task_runner_.get(),
            false,
            GL_LINEAR);
    }

    TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_NearestToNearest)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        ResourceProviderTestTextureMailboxGLFilters::RunTest(
            shared_bitmap_manager_.get(),
            gpu_memory_buffer_manager_.get(),
            main_thread_task_runner_.get(),
            true,
            GL_NEAREST);
    }

    TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_NearestToLinear)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        ResourceProviderTestTextureMailboxGLFilters::RunTest(
            shared_bitmap_manager_.get(),
            gpu_memory_buffer_manager_.get(),
            main_thread_task_runner_.get(),
            true,
            GL_LINEAR);
    }

    TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D_LinearToNearest)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        ResourceProviderTestTextureMailboxGLFilters::RunTest(
            shared_bitmap_manager_.get(),
            gpu_memory_buffer_manager_.get(),
            main_thread_task_runner_.get(),
            false,
            GL_NEAREST);
    }

    TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0,
            gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
        const GLuint64 current_fence_sync = context->GetNextFenceSync();
        unsigned target = GL_TEXTURE_EXTERNAL_OES;

        EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
        EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
        EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);
        EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_, _)).Times(0);

        gpu::Mailbox gpu_mailbox;
        memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
        std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback));

        TextureMailbox mailbox(gpu_mailbox, sync_token, target);

        ResourceId id = resource_provider->CreateResourceFromTextureMailbox(
            mailbox, std::move(callback));
        EXPECT_NE(0u, id);
        EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());

        Mock::VerifyAndClearExpectations(context);

        {
            // Mailbox sync point WaitSyncToken before using the texture.
            EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
            resource_provider->WaitSyncTokenIfNeeded(id);
            Mock::VerifyAndClearExpectations(context);

            unsigned texture_id = 1;

            EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(target, _))
                .WillOnce(Return(texture_id));

            EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);

            ResourceProvider::ScopedReadLockGL lock(resource_provider.get(), id);
            Mock::VerifyAndClearExpectations(context);

            // When done with it, a sync point should be inserted, but no produce is
            // necessary.
            EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
            EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);

            EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
            EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_, _)).Times(0);
        }
    }

    TEST_P(ResourceProviderTest,
        TextureMailbox_WaitSyncTokenIfNeeded_WithSyncToken)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO, 0,
            gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
        const GLuint64 current_fence_sync = context->GetNextFenceSync();
        unsigned target = GL_TEXTURE_2D;

        EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
        EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
        EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);
        EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_, _)).Times(0);

        gpu::Mailbox gpu_mailbox;
        memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
        std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback));

        TextureMailbox mailbox(gpu_mailbox, sync_token, target);

        ResourceId id = resource_provider->CreateResourceFromTextureMailbox(
            mailbox, std::move(callback));
        EXPECT_NE(0u, id);
        EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());

        Mock::VerifyAndClearExpectations(context);

        {
            // First call to WaitSyncTokenIfNeeded should call waitSyncToken.
            EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
            resource_provider->WaitSyncTokenIfNeeded(id);
            Mock::VerifyAndClearExpectations(context);

            // Subsequent calls to WaitSyncTokenIfNeeded shouldn't call waitSyncToken.
            EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
            resource_provider->WaitSyncTokenIfNeeded(id);
            Mock::VerifyAndClearExpectations(context);
        }
    }

    TEST_P(ResourceProviderTest, TextureMailbox_WaitSyncTokenIfNeeded_NoSyncToken)
    {
        // Mailboxing is only supported for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<TextureStateTrackingContext> context_owned(
            new TextureStateTrackingContext);
        TextureStateTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gpu::SyncToken sync_token;
        const GLuint64 current_fence_sync = context->GetNextFenceSync();
        unsigned target = GL_TEXTURE_2D;

        EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
        EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
        EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _, _)).Times(0);
        EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_, _)).Times(0);

        gpu::Mailbox gpu_mailbox;
        memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
        std::unique_ptr<SingleReleaseCallbackImpl> callback = SingleReleaseCallbackImpl::Create(base::Bind(&EmptyReleaseCallback));

        TextureMailbox mailbox(gpu_mailbox, sync_token, target);

        ResourceId id = resource_provider->CreateResourceFromTextureMailbox(
            mailbox, std::move(callback));
        EXPECT_NE(0u, id);
        EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());

        Mock::VerifyAndClearExpectations(context);

        {
            // WaitSyncTokenIfNeeded with empty sync_token shouldn't call waitSyncToken.
            EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
            resource_provider->WaitSyncTokenIfNeeded(id);
            Mock::VerifyAndClearExpectations(context);
        }
    }

    class AllocationTrackingContext3D : public TestWebGraphicsContext3D {
    public:
        MOCK_METHOD0(NextTextureId, GLuint());
        MOCK_METHOD1(RetireTextureId, void(GLuint id));
        MOCK_METHOD2(bindTexture, void(GLenum target, GLuint texture));
        MOCK_METHOD5(texStorage2DEXT,
            void(GLenum target,
                GLint levels,
                GLuint internalformat,
                GLint width,
                GLint height));
        MOCK_METHOD9(texImage2D,
            void(GLenum target,
                GLint level,
                GLenum internalformat,
                GLsizei width,
                GLsizei height,
                GLint border,
                GLenum format,
                GLenum type,
                const void* pixels));
        MOCK_METHOD9(texSubImage2D,
            void(GLenum target,
                GLint level,
                GLint xoffset,
                GLint yoffset,
                GLsizei width,
                GLsizei height,
                GLenum format,
                GLenum type,
                const void* pixels));
        MOCK_METHOD9(asyncTexImage2DCHROMIUM,
            void(GLenum target,
                GLint level,
                GLenum internalformat,
                GLsizei width,
                GLsizei height,
                GLint border,
                GLenum format,
                GLenum type,
                const void* pixels));
        MOCK_METHOD9(asyncTexSubImage2DCHROMIUM,
            void(GLenum target,
                GLint level,
                GLint xoffset,
                GLint yoffset,
                GLsizei width,
                GLsizei height,
                GLenum format,
                GLenum type,
                const void* pixels));
        MOCK_METHOD8(compressedTexImage2D,
            void(GLenum target,
                GLint level,
                GLenum internalformat,
                GLsizei width,
                GLsizei height,
                GLint border,
                GLsizei image_size,
                const void* data));
        MOCK_METHOD1(waitAsyncTexImage2DCHROMIUM, void(GLenum));
        MOCK_METHOD4(createImageCHROMIUM,
            GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
        MOCK_METHOD1(destroyImageCHROMIUM, void(GLuint));
        MOCK_METHOD2(bindTexImage2DCHROMIUM, void(GLenum, GLint));
        MOCK_METHOD2(releaseTexImage2DCHROMIUM, void(GLenum, GLint));

        // We're mocking bindTexture, so we override
        // TestWebGraphicsContext3D::texParameteri to avoid assertions related to the
        // currently bound texture.
        virtual void texParameteri(GLenum target, GLenum pname, GLint param) { }
    };

    TEST_P(ResourceProviderTest, TextureAllocation)
    {
        // Only for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        std::unique_ptr<AllocationTrackingContext3D> context_owned(
            new StrictMock<AllocationTrackingContext3D>);
        AllocationTrackingContext3D* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(2, 2);
        gfx::Vector2d offset(0, 0);
        ResourceFormat format = RGBA_8888;
        ResourceId id = 0;
        uint8_t pixels[16] = { 0 };
        int texture_id = 123;

        // Lazy allocation. Don't allocate when creating the resource.
        id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());

        EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
        resource_provider->CreateForTesting(id);

        EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
        resource_provider->DeleteResource(id);

        Mock::VerifyAndClearExpectations(context);

        // Do allocate when we set the pixels.
        id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());

        EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
        EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _)).Times(1);
        EXPECT_CALL(*context, texSubImage2D(_, _, _, _, 2, 2, _, _, _)).Times(1);
        resource_provider->CopyToResource(id, pixels, size);

        EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
        resource_provider->DeleteResource(id);

        Mock::VerifyAndClearExpectations(context);
    }

    TEST_P(ResourceProviderTest, TextureAllocationHint)
    {
        // Only for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        std::unique_ptr<AllocationTrackingContext3D> context_owned(
            new StrictMock<AllocationTrackingContext3D>);
        AllocationTrackingContext3D* context = context_owned.get();
        context->set_support_texture_storage(true);
        context->set_support_texture_usage(true);
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(2, 2);

        const ResourceFormat formats[2] = { RGBA_8888, BGRA_8888 };
        const ResourceProvider::TextureHint hints[4] = {
            ResourceProvider::TEXTURE_HINT_DEFAULT,
            ResourceProvider::TEXTURE_HINT_IMMUTABLE,
            ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
            ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
        };
        for (size_t i = 0; i < arraysize(formats); ++i) {
            for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
                // Lazy allocation. Don't allocate when creating the resource.
                ResourceId id = resource_provider->CreateResource(
                    size, hints[texture_id - 1], formats[i], gfx::ColorSpace());

                EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
                EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
                bool is_immutable_hint = hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_IMMUTABLE;
                bool support_immutable_texture = is_immutable_hint && formats[i] == RGBA_8888;
                EXPECT_CALL(*context, texStorage2DEXT(_, _, _, 2, 2))
                    .Times(support_immutable_texture ? 1 : 0);
                EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _))
                    .Times(support_immutable_texture ? 0 : 1);
                resource_provider->AllocateForTesting(id);

                EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
                resource_provider->DeleteResource(id);

                Mock::VerifyAndClearExpectations(context);
            }
        }
    }

    TEST_P(ResourceProviderTest, TextureAllocationHint_BGRA)
    {
        // Only for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        std::unique_ptr<AllocationTrackingContext3D> context_owned(
            new StrictMock<AllocationTrackingContext3D>);
        AllocationTrackingContext3D* context = context_owned.get();
        context->set_support_texture_format_bgra8888(true);
        context->set_support_texture_storage(true);
        context->set_support_texture_usage(true);
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        gfx::Size size(2, 2);
        const ResourceFormat formats[2] = { RGBA_8888, BGRA_8888 };

        const ResourceProvider::TextureHint hints[4] = {
            ResourceProvider::TEXTURE_HINT_DEFAULT,
            ResourceProvider::TEXTURE_HINT_IMMUTABLE,
            ResourceProvider::TEXTURE_HINT_FRAMEBUFFER,
            ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER,
        };
        for (size_t i = 0; i < arraysize(formats); ++i) {
            for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
                // Lazy allocation. Don't allocate when creating the resource.
                ResourceId id = resource_provider->CreateResource(
                    size, hints[texture_id - 1], formats[i], gfx::ColorSpace());

                EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
                EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
                bool is_immutable_hint = hints[texture_id - 1] & ResourceProvider::TEXTURE_HINT_IMMUTABLE;
                EXPECT_CALL(*context, texStorage2DEXT(_, _, _, 2, 2))
                    .Times(is_immutable_hint ? 1 : 0);
                EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _))
                    .Times(is_immutable_hint ? 0 : 1);
                resource_provider->AllocateForTesting(id);

                EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
                resource_provider->DeleteResource(id);

                Mock::VerifyAndClearExpectations(context);
            }
        }
    }

    TEST_P(ResourceProviderTest, Image_GLTexture)
    {
        // Only for GL textures.
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;
        std::unique_ptr<AllocationTrackingContext3D> context_owned(
            new StrictMock<AllocationTrackingContext3D>);
        AllocationTrackingContext3D* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        const int kWidth = 2;
        const int kHeight = 2;
        gfx::Size size(kWidth, kHeight);
        ResourceFormat format = RGBA_8888;
        ResourceId id = 0;
        const unsigned kTextureId = 123u;
        const unsigned kImageId = 234u;

        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));

        id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
            gfx::ColorSpace());

        EXPECT_CALL(*context, NextTextureId())
            .WillOnce(Return(kTextureId))
            .RetiresOnSaturation();
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId))
            .Times(3)
            .RetiresOnSaturation();
        EXPECT_CALL(*context, createImageCHROMIUM(_, kWidth, kHeight, GL_RGBA))
            .WillOnce(Return(kImageId))
            .RetiresOnSaturation();
        {
            ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
                resource_provider.get(), id);
            EXPECT_TRUE(lock.GetGpuMemoryBuffer());
        }

        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
            .Times(1)
            .RetiresOnSaturation();
        {
            ResourceProvider::ScopedSamplerGL lock_gl(
                resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
            EXPECT_EQ(kTextureId, lock_gl.texture_id());
        }

        {
            ResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
                resource_provider.get(), id);
            EXPECT_TRUE(lock.GetGpuMemoryBuffer());
        }

        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(1).RetiresOnSaturation();
        EXPECT_CALL(*context, releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*context, RetireTextureId(kTextureId))
            .Times(1)
            .RetiresOnSaturation();
        {
            ResourceProvider::ScopedSamplerGL lock_gl(
                resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
            EXPECT_EQ(kTextureId, lock_gl.texture_id());
        }

        EXPECT_CALL(*context, destroyImageCHROMIUM(kImageId))
            .Times(1)
            .RetiresOnSaturation();
    }

    TEST_P(ResourceProviderTest, CompressedTextureETC1Allocate)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<AllocationTrackingContext3D> context_owned(
            new AllocationTrackingContext3D);
        AllocationTrackingContext3D* context = context_owned.get();
        context_owned->set_support_compressed_texture_etc1(true);
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        gfx::Size size(4, 4);
        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));
        int texture_id = 123;

        ResourceId id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, ETC1, gfx::ColorSpace());
        EXPECT_NE(0u, id);
        EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
        resource_provider->AllocateForTesting(id);

        EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
        resource_provider->DeleteResource(id);
    }

    TEST_P(ResourceProviderTest, CompressedTextureETC1Upload)
    {
        if (GetParam() != ResourceProvider::RESOURCE_TYPE_GL_TEXTURE)
            return;

        std::unique_ptr<AllocationTrackingContext3D> context_owned(
            new AllocationTrackingContext3D);
        AllocationTrackingContext3D* context = context_owned.get();
        context_owned->set_support_compressed_texture_etc1(true);
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();

        gfx::Size size(4, 4);
        std::unique_ptr<ResourceProvider> resource_provider(
            base::MakeUnique<ResourceProvider>(
                context_provider.get(), shared_bitmap_manager_.get(),
                gpu_memory_buffer_manager_.get(), nullptr, 0, 1,
                kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                kEnableColorCorrectRendering,
                DefaultBufferToTextureTargetMapForTesting()));
        int texture_id = 123;
        uint8_t pixels[8];

        ResourceId id = resource_provider->CreateResource(
            size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, ETC1, gfx::ColorSpace());
        EXPECT_NE(0u, id);
        EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
        EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
        EXPECT_CALL(*context,
            compressedTexImage2D(
                _, 0, _, size.width(), size.height(), _, _, _))
            .Times(1);
        resource_provider->CopyToResource(id, pixels, size);

        EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
        resource_provider->DeleteResource(id);
    }

    INSTANTIATE_TEST_CASE_P(
        ResourceProviderTests,
        ResourceProviderTest,
        ::testing::Values(ResourceProvider::RESOURCE_TYPE_GL_TEXTURE,
            ResourceProvider::RESOURCE_TYPE_BITMAP));

    class TextureIdAllocationTrackingContext : public TestWebGraphicsContext3D {
    public:
        GLuint NextTextureId() override
        {
            base::AutoLock lock(namespace_->lock);
            return namespace_->next_texture_id++;
        }
        void RetireTextureId(GLuint) override { }
        GLuint PeekTextureId()
        {
            base::AutoLock lock(namespace_->lock);
            return namespace_->next_texture_id;
        }
    };

    TEST(ResourceProviderTest, TextureAllocationChunkSize)
    {
        std::unique_ptr<TextureIdAllocationTrackingContext> context_owned(
            new TextureIdAllocationTrackingContext);
        TextureIdAllocationTrackingContext* context = context_owned.get();
        auto context_provider = TestContextProvider::Create(std::move(context_owned));
        context_provider->BindToCurrentThread();
        auto shared_bitmap_manager = base::MakeUnique<TestSharedBitmapManager>();

        gfx::Size size(1, 1);
        ResourceFormat format = RGBA_8888;

        {
            size_t kTextureAllocationChunkSize = 1;
            std::unique_ptr<ResourceProvider> resource_provider(
                base::MakeUnique<ResourceProvider>(
                    context_provider.get(), shared_bitmap_manager.get(), nullptr,
                    nullptr, 0, kTextureAllocationChunkSize,
                    kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                    kEnableColorCorrectRendering,
                    DefaultBufferToTextureTargetMapForTesting()));

            ResourceId id = resource_provider->CreateResource(
                size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
                gfx::ColorSpace());
            resource_provider->AllocateForTesting(id);
            Mock::VerifyAndClearExpectations(context);

            DCHECK_EQ(2u, context->PeekTextureId());
            resource_provider->DeleteResource(id);
        }

        {
            size_t kTextureAllocationChunkSize = 8;
            std::unique_ptr<ResourceProvider> resource_provider(
                base::MakeUnique<ResourceProvider>(
                    context_provider.get(), shared_bitmap_manager.get(), nullptr,
                    nullptr, 0, kTextureAllocationChunkSize,
                    kDelegatedSyncPointsRequired, kUseGpuMemoryBufferResources,
                    kEnableColorCorrectRendering,
                    DefaultBufferToTextureTargetMapForTesting()));

            ResourceId id = resource_provider->CreateResource(
                size, ResourceProvider::TEXTURE_HINT_IMMUTABLE, format,
                gfx::ColorSpace());
            resource_provider->AllocateForTesting(id);
            Mock::VerifyAndClearExpectations(context);

            DCHECK_EQ(10u, context->PeekTextureId());
            resource_provider->DeleteResource(id);
        }
    }

} // namespace
} // namespace cc
